package com.itheima;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

/**
 * @program: curator-zk
 * @description:
 * @author: zhanghz001
 * @create: 2021-02-07 16:46
 **/
public class CuratorCURDTest0 {
    CuratorFramework client = null;
    
    @Before
    public void before() {
        /*
         *
         * @param connectString       连接字符串。zk server 地址和端口 "192.168.149.135:2181,192.168.149.136:2181"
         * @param sessionTimeoutMs    会话超时时间 单位ms
         * @param connectionTimeoutMs 连接超时时间 单位ms
         * @param retryPolicy         重试策略
         */
       /* //重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
        //1.第一种方式
        CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.149.135:2181",
                60 * 1000, 15 * 1000, retryPolicy);*/
        
        //重试策略
        //@param baseSleepTimeMs 间隔时间
        //         @param maxRetries 重试次数
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(
                3000, 3);
        
        //第二种方式
        client = CuratorFrameworkFactory.builder()
                .connectString("192.168.23.129:2181")
                .sessionTimeoutMs(60 * 1000)
                .connectionTimeoutMs(10 * 1000)
                .retryPolicy(retryPolicy)
                //命名空间,都是在这个节点下
                .namespace("itheima1")
                .build();
        //开启连接
        client.start();
    }
    
    @After
    public void after() {
        if (client != null) {
            client.close();
        }
    }

//==============================create=============================================================================
    
    /**
     * 创建节点：create 持久 临时 顺序 数据
     * 1. 基本创建 ：create().forPath(""),其中data默认为本机的ip地址
     * 2. 创建节点 带有数据:create().forPath("",data)
     * 3. 设置节点的类型：create().withMode().forPath("",data)
     * 4. 创建多级节点  /app1/p1 ：create().creatingParentsIfNeeded().forPath("",data)
     */
    @Test
    public void create1() throws Exception {
        // * 1. 基本创建 ：create().forPath(""),其中data默认为本机的ip地址
        String path = client.create().forPath("/bb");
        //"/bb"
        System.out.println(path);
    }
    
    @Test
    public void create2() throws Exception {
        //2. 创建节点 带有数据:create().forPath("",data)
        String path = client.create().forPath("/create2", "create2".getBytes());
        System.out.println(path);
    }
    
    @Test
    public void create3() throws Exception {
        //3. 设置节点的类型：create().withMode().forPath("",data)
        String path;
        path = client.create().withMode(CreateMode.EPHEMERAL)
                .forPath("/create31", "EPHEMERAL".getBytes());
        System.out.println(path);
        
        path = client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
                .forPath("/create32", "EPHEMERAL_SEQUENTIAL".getBytes());
        System.out.println(path);
        
        path = client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL)
                .forPath("/create33", "PERSISTENT_SEQUENTIAL".getBytes());
        System.out.println(path);
        
    }
    
    @Test
    public void create4() throws Exception {
        // 4. 创建多级节点  /app1/p1 ：create().creatingParentsIfNeeded().forPath("",data)
        String path = client.create().creatingParentsIfNeeded()
                .forPath("/create4/create41/create42", "create4".getBytes());
        System.out.println(path);
    }
    //===========================get================================================================================
    
    /**
     * 查询节点：
     * 1. 查询数据：get: getData().forPath()
     * 2. 查询子节点： ls: getChildren().forPath()
     * 3. 查询节点状态信息：ls -s:getData().storingStatIn(状态对象).forPath()
     */
    @Test
    public void get1() throws Exception {
        //查询数据：get: getData().forPath()
        byte[] bytes = client.getData().forPath("/create4/create41/create42");
        System.out.println(new String(bytes));
    }
    
    @Test
    public void get2() throws Exception {
        //
        List<String> strings = client.getChildren().forPath("/");
        System.out.println(strings);
    }
    
    @Test
    public void get3() throws Exception {
        //查询节点状态信息：ls -s:getData().storingStatIn(状态对象).forPath()
        Stat status = new Stat();
        System.out.println(status);
        byte[] bytes = client.getData().storingStatIn(status).forPath("/create2");
        System.out.println(status);
        System.out.println(new String(bytes));
        
    }
    
    //===========================set================================================================================
    
    /**
     * 修改数据
     * 1. 基本修改数据：setData().forPath()
     * 2. 根据版本修改: setData().withVersion().forPath()
     * * version 是通过查询出来的。目的就是为了让其他客户端或者线程不干扰我。
     *
     * @throws Exception
     */
    
    @Test
    public void set1() throws Exception {
        //
        // * 1. 基本修改数据：setData().forPath()
        Stat stat = client.setData().forPath("/create2", "111".getBytes());
        System.out.println(stat);
    }
    
    @Test
    public void set2() throws Exception {
        //     根据版本修改: setData().withVersion().forPath()
        //* version 是通过查询出来的。目的就是为了让其他客户端或者线程不干扰我。
        Stat status = new Stat();
        client.getData().storingStatIn(status).forPath("/create2");
        int version = status.getVersion();
        Stat stat = client.setData().withVersion(version).forPath("/create2", "222222222".getBytes());
        System.out.println(stat);
    }
    
    //===========================delete================================================================================
    
    /**
     * 删除节点： delete deleteall
     * 1. 删除单个节点:delete().forPath("/app1");
     * 2. 删除带有子节点的节点:delete().deletingChildrenIfNeeded().forPath("/app1");
     * 3. 必须成功的删除:为了防止网络抖动。本质就是重试。  client.delete().guaranteed().forPath("/app2");
     * 4. 回调：inBackground
     *
     * @throws Exception
     */
    
    @Test
    public void delete1() throws Exception {
        //
        // * 1. 删除单个节点:delete().forPath("/app1");
        client.delete().forPath("/aa");
    }
    
    @Test
    public void delete2() throws Exception {
        // 删除带有子节点的节点:delete().deletingChildrenIfNeeded().forPath("/app1");
        client.delete().deletingChildrenIfNeeded().forPath("/bb");
    }
    
    @Test
    public void delete3() throws Exception {
        //确定要是删除
        client.delete().guaranteed().forPath("/create4/create41/create42");
    }
    
    @Test
    public void delete4() throws Exception {
        //    删除回调,删除回调inBackground,需要配合确认删除guaranteed,这样才能删除干净
        BackgroundCallback backgroundCallback = new BackgroundCallback() {
            @Override
            public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
                System.out.println("删除了回调");
                // System.out.println(curatorFramework == client);
                System.out.println(curatorEvent);
            }
        };
        client.delete().guaranteed().inBackground(backgroundCallback).forPath("/create4");
    }
}
